package com.lyn.serialPort;

import android.os.SystemClock;
import android.util.Log;

import com.lyn.serialPort.entity.Device;
import com.lyn.serialPort.interfaces.Port;

import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;

/**
 * Android串口
 *
 * @author longyn 2022/12/24
 * @version 1.0.0
 */
 class SerialPort implements Port {

    private static final String TAG = "SerialPort";

    private String name;
    private String path;
    private int baudRate;
    private FileDescriptor fileDescriptor;
    private InputStream inputStream;
    private OutputStream outputStream;


    @Override
    public boolean open(String name, int baudRate) {
        try {
            this.name = name;
            this.baudRate = baudRate;
            Device device = SerialPortFinder.getDevice(name);
            if (device != null) {
                path = device.file.getAbsolutePath();
                if (!device.file.canRead() || !device.file.canWrite()) {
                    if (chmod666(device.file)) {
                        return false;
                    }
                }
                fileDescriptor = openSerialPort(path, baudRate, 0);
                if (fileDescriptor != null) {
                    Log.i(TAG, "openSerialPort success");
                    this.inputStream = new FileInputStream(fileDescriptor);
                    this.outputStream = new FileOutputStream(fileDescriptor);
                    return true;
                }
            }
        } catch (Throwable e) {
            e.printStackTrace();
        }
        Log.i(TAG, "openSerialPort fail");
        return false;
    }

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public String getPath() {
        return this.path;
    }

    @Override
    public int getBaudRate() {
        return this.baudRate;
    }

    @Override
    public FileDescriptor getFileDescriptor() {
        return this.fileDescriptor;
    }

    @Override
    public InputStream getInputStream() {
        return this.inputStream;
    }

    @Override
    public OutputStream getOutputStream() {
        return this.outputStream;
    }



    @Override
    public boolean close() {
        try {
            int closeSerialPort = closeSerialPort();
            Log.i(TAG, "closeSerialPort:" + closeSerialPort);
            return true;
        } catch (Throwable e) {
            e.printStackTrace();
        }
        return false;
    }


    private native FileDescriptor openSerialPort(String path, int baudRate, int flags);

    private native int closeSerialPort();

    static {
        try {
            System.loadLibrary("SerialPort");
        } catch (Throwable e) {
            e.printStackTrace();
        }
    }

    private boolean chmod666(File file) {
        if (null != file && file.exists()) {
            try {
                Process su = Runtime.getRuntime().exec("/system/bin/su");
                String cmd = "chmod 666 " + file.getAbsolutePath() + "\nexit\n";
                su.getOutputStream().write(cmd.getBytes());
                if (0 == su.waitFor() && file.canRead() && file.canWrite() && file.canExecute()) {
                    return true;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            return false;
        }
        return false;
    }

    @Override
    public void send(byte[] data) {
        send(data,0);
    }

    @Override
    public byte[] send(byte[] data, long timeOut) {
        try {
            outputStream.write(data);
            if (timeOut <= 0) {
                return null;
            }
            long time = System.currentTimeMillis();
            int receiveLength = 0;
            while (System.currentTimeMillis() - time < timeOut) {
                int available = inputStream.available();
                if (available > 0 && available == receiveLength) {
                    byte[] returnData = new byte[available];
                    int read = inputStream.read(returnData);
                    return returnData;
                } else {
                    receiveLength = available;
                }
                SystemClock.sleep(10);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
